using UnityEngine;
using UnityEngine.AI;

namespace Gj
{
    [RequireComponent(typeof(NavMeshAgent))]
    public class AgentPart : BasePart
    {
        private MoveStatus _lastMoveStatus = MoveStatus.None;
        private float _maxFrameTime = 1f;
        private float _minFrameTime = 0.2f;
        private Vector3 _moveDirection = Vector3.zero;
        private Vector3 _movePosition = Vector3.zero;
        private MoveStatus _moveStatus = MoveStatus.None;
        private UnityObject _moveTarget;
        private NavMeshAgent _navMeshAgent;
        private Notice _notice;
        private Vector3 _offsetPosition = Vector3.zero;

        private Move _self;
        private float _surplusFrameTime = 0;
        private float _totalFrameTime = 0;
        private UnityObject _vehicle;
        private Vector3 vehicleLastPosition;
        private Vector3 vehicleOffsetPosition;

        public NavMeshAgent NavMeshAgent
        {
            get
            {
                if (_navMeshAgent == null) _navMeshAgent = GetComponent<NavMeshAgent>();
                return _navMeshAgent;
            }
        }

        public void Update()
        {
            if (_moveStatus == MoveStatus.None) return;
            switch (_moveStatus)
            {
                case MoveStatus.Direction:
                    // 0.5是测试下来不会产生bug的固定值，可以更小，但是不可以小于stoppingDistance不然会停止不动
                    NavMeshAgent.SetDestination(_self.GetPosition() + _moveDirection * 0.5f);
                    break;
                case MoveStatus.Position:
                    if (NavMeshAgent.remainingDistance <= 0.1) Stop();
                    break;
                case MoveStatus.Target:
                    NavMeshAgent.SetDestination(_moveTarget.GetPosition());
                    break;
            }
        }

        public void LateUpdate()
        {
            if (_self != null)
            {
                var lastPosition = _self.GetPosition();
                var lastRotation = _self.GetRotation();
                var p = UpdateAgent(transform.position);
                if (_moveStatus != MoveStatus.None && _moveStatus != MoveStatus.Stop)
                    if (Vector3.Distance(p, lastPosition) > 0)
                    {
                        var _r = Quaternion.LookRotation(p - lastPosition);
                        var r = Quaternion.RotateTowards(lastRotation, _r, _self.GetRotateSpeed() * Time.deltaTime);
                        _self.SetRotation(r);
                        _self.UpdateRotation(r);
                    }

                _self.SetPosition(p);
                _self.UpdatePosition(p);
            }
        }

        public bool IsMoving()
        {
            switch (_moveStatus)
            {
                case MoveStatus.Direction:
                case MoveStatus.Position:
                case MoveStatus.Target:
                    return true;
                default:
                    return false;
            }
        }

        public void Set(Notice notice)
        {
            _notice = notice;
        }

        public void Init(Move target)
        {
            _self = target;
            NavMeshAgent.Warp(_self.GetPosition());
            NavMeshAgent.speed = _self.GetMoveSpeed();
            NavMeshAgent.angularSpeed = 0;
            NavMeshAgent.acceleration = 10;
            NavMeshAgent.radius = _self.GetRadius();
            NavMeshAgent.height = _self.GetHeight();
            NavMeshAgent.stoppingDistance = 0;
            NavMeshAgent.updateRotation = false;
            _moveStatus = MoveStatus.Stop;
        }

        public void Clear()
        {
            _self = null;
            _moveStatus = MoveStatus.None;
        }

        public void SetVehicle(UnityObject e)
        {
            _vehicle = e;
        }

        public void MoveDirection(Vector3 d)
        {
            _moveDirection = d;
            _moveStatus = MoveStatus.Direction;
        }

        public void Stop()
        {
            NavMeshAgent.isStopped = true;
            _lastMoveStatus = _moveStatus;
            _moveStatus = MoveStatus.Stop;
        }

        public void Resume()
        {
            NavMeshAgent.isStopped = false;
            _moveStatus = _lastMoveStatus;
        }

        public void Ready()
        {
            NavMeshAgent.isStopped = false;
        }

        public void Cancel()
        {
            NavMeshAgent.isStopped = true;
            _moveStatus = MoveStatus.None;
        }

        public void ChangeSpeed()
        {
            NavMeshAgent.speed = _self.GetMoveSpeed();
        }

        /// <summary>
        ///     移动到指定坐标
        /// </summary>
        /// <param name="position"></param>
        public void MovePosition(Vector3 position)
        {
            if (_moveStatus == MoveStatus.Stop) Ready();
            _movePosition = position;
            NavMeshAgent.SetDestination(position);
            NavMeshAgent.stoppingDistance = 0;
            _moveStatus = MoveStatus.Position;
        }

        /// <summary>
        ///     移动到指定目标
        /// </summary>
        /// <param name="obj"></param>
        public void MoveTarget(UnityObject obj)
        {
            if (_moveStatus == MoveStatus.Stop) Ready();
            _moveTarget = obj;
            NavMeshAgent.SetDestination(obj.GetPosition());
            NavMeshAgent.stoppingDistance = _self.GetRadius() + obj.GetRadius() + 0.05f;
            _moveStatus = MoveStatus.Target;
        }

        public void UpdatePosition(Vector3 p)
        {
            NavMeshAgent.Warp(p);
        }

        private Vector3 UpdateAgent(Vector3 p)
        {
            if (_vehicle != null)
            {
                var _mp = _vehicle.GetPosition();
                var _p = IsMoving() ? p : _self.GetPosition();
                vehicleOffsetPosition = _mp - vehicleLastPosition;
                vehicleLastPosition = _mp;
                if (Vector3.Distance(_mp, _p) > _vehicle.GetRadius()) return p;
                p = _p + vehicleOffsetPosition;
            }

            return p;
        }

        public interface Notice
        {
            void StopWithError();
            void StopWithSuccess();
        }
    }
}